import math
import utime
import gc
import ure

class NumberedMusicalNotation:
    def __init__(self, pwm):
        self.pwm = pwm

        frequency = []

        for i in range(12):
            f = 262 * math.pow(2, i/12)
            frequency.append(f)

        self.scaleDict = {
            '0': 0,
            '1': int(frequency[0]),
            'b2': int(frequency[1]),
            '2': int(frequency[2]),
            'b3': int(frequency[3]),
            '3': int(frequency[4]),
            '4': int(frequency[5]),
            'b5': int(frequency[6]),
            '5': int(frequency[7]),
            'b6': int(frequency[8]),
            '6': int(frequency[9]),
            'b7': int(frequency[10]),
            '7': int(frequency[11])
        }

        self.beatSpeed = 120
        self.beatNumber = 4

    def tone(self, freq, still):
        self.pwm.duty(50)
        self.pwm.freq(freq)
        utime.sleep_ms(int(still))

    def noTone(self):
        self.pwm.duty(0)

    def speed(self, beatSpeed):
        self.beatSpeed = beatSpeed

    def timeSignature(self, beatNumber):
        self.beatNumber = beatNumber

    def playNotation(self, monophonic, preAdd = 0):
        scaleKey = ''

        if(monophonic.find('b') != -1):
            scaleKey += 'b'

        digitals = ure.search(r'\d+', monophonic)
        scaleKey += digitals.group(0)
        upNumber = monophonic.count('+')
        downNumber = monophonic.count('-')

        realRate = self.scaleDict[scaleKey] * math.pow(2, upNumber) * (1 / math.pow(2, downNumber))

        wholeNote = (60 / self.beatSpeed) * self.beatNumber
        upNumber = monophonic.count('.')
        downNumber = monophonic.count('_')
        voiceTime = wholeNote * (1 / 4) * (1 / math.pow(2, downNumber))
        if(upNumber == 1):
            voiceTime *= 2
        elif(upNumber == 3):
            voiceTime *= 4

        if(realRate != 0):
            self.tone(int(realRate * math.pow(2, preAdd)), int(voiceTime * 1000))
        else:
            utime.sleep_ms(int(voiceTime * 1000))
        
        self.noTone()

        gc.collect()